import pytest
import asyncio
from typing import AsyncGenerator, Generator
from httpx import AsyncClient
import asyncpg
import redis.asyncio as redis
from faker import Faker
from src.main import app
from src.core.config import settings
from src.core.dependencies import init_db_pool, init_redis_client

fake = Faker()


@pytest.fixture(scope="session")
def event_loop() -> Generator:
    """Create event loop for async tests."""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest.fixture(scope="session")
async def db_pool():
    """Create database connection pool for tests."""
    # Use test database
    test_db_url = settings.DATABASE_URL.replace("ai_writing_platform", "ai_writing_platform_test")
    pool = await asyncpg.create_pool(
        test_db_url.replace("postgresql+asyncpg://", "postgresql://"),
        min_size=5,
        max_size=10
    )
    
    # Run migrations
    async with pool.acquire() as connection:
        await connection.execute("""
            CREATE TABLE IF NOT EXISTS users (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                email VARCHAR(255) UNIQUE NOT NULL,
                username VARCHAR(50) UNIQUE NOT NULL,
                password_hash VARCHAR(255) NOT NULL,
                phone VARCHAR(20),
                role VARCHAR(20) DEFAULT 'user',
                is_active BOOLEAN DEFAULT true,
                email_verified BOOLEAN DEFAULT false,
                created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
                last_login_at TIMESTAMP WITH TIME ZONE
            )
        """)
    
    yield pool
    
    # Cleanup
    async with pool.acquire() as connection:
        await connection.execute("DROP TABLE IF EXISTS users CASCADE")
    
    await pool.close()


@pytest.fixture(scope="session")
async def redis_client():
    """Create Redis client for tests."""
    # Use test database (db=1)
    test_redis_url = settings.REDIS_URL.replace("/0", "/1")
    client = redis.from_url(test_redis_url, encoding="utf-8", decode_responses=False)
    
    yield client
    
    # Cleanup
    await client.flushdb()
    await client.close()


@pytest.fixture
async def client() -> AsyncGenerator[AsyncClient, None]:
    """Create async HTTP client for tests."""
    async with AsyncClient(app=app, base_url="http://test") as ac:
        yield ac


@pytest.fixture
def user_data():
    """Generate test user data."""
    return {
        "email": fake.email(),
        "username": fake.user_name(),
        "password": "Test123456",
        "phone": "13800138000"
    }


@pytest.fixture
async def registered_user(client: AsyncClient, user_data):
    """Create a registered user for tests."""
    response = await client.post("/api/v1/auth/register", json=user_data)
    assert response.status_code == 201
    data = response.json()
    return {
        **user_data,
        "token": data["data"]["token"],
        "user": data["data"]["user"]
    }


@pytest.fixture
def auth_headers(registered_user):
    """Get authorization headers with valid token."""
    return {
        "Authorization": f"Bearer {registered_user['token']}"
    }